
class CompressedGene:
    def __init__(self, gene:str) -> None:
        self._compress(gene)

    def _compress(self, gene:str) -> None:
        self.bit_string:int = 1
        for x in gene.upper():
            self.bit_string <<= 2
            if x == "A":
                self.bit_string |= 0b00
            elif x == "C":
                self.bit_string |= 0b01
            elif x == "G":
                self.bit_string |= 0b10
            elif x == "T":
                self.bit_string |= 0b11
            else:
                raise Exception(f"Invalid char {x} {gene}")

    def decompress(self) -> str:
        gene:str = ""
        for i in range(0, self.bit_string.bit_length()-1,2):
            bits:int = self.bit_string >> i & 0b11
            if bits == 0b00:
                gene += "A"
            elif bits == 0b01:
                gene += "C"
            elif bits == 0b10:
                gene += "G"
            elif bits == 0b11:
                gene += "T"
        return gene[::-1]

    def __str__(self) -> str:
        return self.decompress()

if __name__ == "__main__":
    from sys import getsizeof

    original:str = "TAGGGATTAACCGTTATATATATATATATGGGCCCCATATATATATATATAT"*100
    print(f"original is {getsizeof(original)} bytes")
    compressed = CompressedGene(original)
    print(f"compressed is {getsizeof(compressed)} bytes")
    print(compressed)
    print(compressed.decompress() == original)
        